<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/3/8
 * Time: 19:27
 */

namespace app\api\Service;

use app\api\library\SecretSys;
use app\common\model\Bets;
use app\common\model\GameLol;
use app\common\model\LotteryGame;
use fast\Http;
use think\Cache;
use app\common\model\LotteryLol;
use app\common\model\LotteryDota2;
use app\common\model\LotteryPvp;
use app\common\model\GameDota2;
use app\common\model\GamePvp;
use think\Db;
use think\Exception;
use app\common\model\Config;
use think\exception\PDOException;
use think\Hook;
use think\Queue;

class GameService
{
    public $gameLol;
    public $gameDota2;
    public $gamePvp;

    public $kaijiang;
    public $userService;
    public $websocket;
    public $pvpkaijiang;
    protected $url = 'http://192.168.10.10:9502';

    protected $generalRoom  = -1;
    protected $memberRoom   = 50000;
    protected $vipRoom      = 100000;
    protected $roomLevel = [
        'general'   =>'普通厅',
        'member'    => '会员厅',
        'vip'       => '贵宾厅'
    ];
    public $maxHp = 100;
    public $minHp = 1;

    public function __construct()
    {
        $this->gameLol = new GameLol();
        $this->gameDota2 = new GameDota2();
        $this->gamePvp  = new GamePvp();
        $this->userService = new UserService();
        $this->websocket = new WebsocketService();

        $this->kaijiang = [
            [date('Y-m-d H:00:16',time()),date('Y-m-d H:03:26',time())],
            [date('Y-m-d H:03:36',time()),date('Y-m-d H:06:46',time())],
            [date('Y-m-d H:06:56',time()),date('Y-m-d H:10:06',time())],

            [date('Y-m-d H:10:16',time()),date('Y-m-d H:13:26',time())],
            [date('Y-m-d H:13:36',time()),date('Y-m-d H:16:46',time())],
            [date('Y-m-d H:16:56',time()),date('Y-m-d H:20:06',time())],

            [date('Y-m-d H:20:16',time()),date('Y-m-d H:23:26',time())],
            [date('Y-m-d H:23:36',time()),date('Y-m-d H:26:46',time())],
            [date('Y-m-d H:26:56',time()),date('Y-m-d H:30:06',time())],

            [date('Y-m-d H:30:16',time()),date('Y-m-d H:33:26',time())],
            [date('Y-m-d H:33:36',time()),date('Y-m-d H:36:46',time())],
            [date('Y-m-d H:36:56',time()),date('Y-m-d H:40:06',time())],

            [date('Y-m-d H:40:16',time()),date('Y-m-d H:43:26',time())],
            [date('Y-m-d H:43:36',time()),date('Y-m-d H:46:46',time())],
            [date('Y-m-d H:46:56',time()),date('Y-m-d H:50:06',time())],

            [date('Y-m-d H:50:16',time()),date('Y-m-d H:53:26',time())],
            [date('Y-m-d H:53:36',time()),date('Y-m-d H:56:46',time())],
            [date('Y-m-d H:56:56',time()),date('Y-m-d H:00:06',time())],
        ];
        $this->pvpkaijiang = [
            [date('Y-m-d H:00:06',time()),date('Y-m-d H:04:56',time())],
            [date('Y-m-d H:05:06',time()),date('Y-m-d H:09:56',time())],

            [date('Y-m-d H:10:06',time()),date('Y-m-d H:14:56',time())],
            [date('Y-m-d H:15:06',time()),date('Y-m-d H:19:56',time())],

            [date('Y-m-d H:20:06',time()),date('Y-m-d H:24:56',time())],
            [date('Y-m-d H:25:06',time()),date('Y-m-d H:29:56',time())],

            [date('Y-m-d H:30:06',time()),date('Y-m-d H:34:56',time())],
            [date('Y-m-d H:35:06',time()),date('Y-m-d H:39:56',time())],

            [date('Y-m-d H:40:06',time()),date('Y-m-d H:44:56',time())],
            [date('Y-m-d H:45:06',time()),date('Y-m-d H:49:56',time())],

            [date('Y-m-d H:50:06',time()),date('Y-m-d H:54:56',time())],
            [date('Y-m-d H:55:06',time()),date('Y-m-d H:59:56',time())],
        ];


    }


    /**
     * 取出开奖结果
     * @param $game
     */
    public function getLotteries($game)
    {
        if( $game == 'pvp' ){
            return $this->isLotteriesCache($game,$this->kaijiang);
        }else{
            return $this->isLotteriesCache($game,$this->pvpkaijiang);
        }

    }


    /**
     * 开奖
     * @param $game string 游戏
     */
    public function lottery($game)
    {
        $lotteryGame = 'randomRet'.$game;
        $this->$lotteryGame();
    }

    /**
     * 首先更新开奖结果，再插入待开奖数据（便于后台手动更改开奖结果）
     * 随机开奖结果（暂时）
     * @return bool
     */
    private function randomRetLOL()
    {
        if( Cache::has('lol') ){ //结果固定保存在cookie中
            $identifier = Cache::get('lol');
        }else{
            $identifier = $this->gameLol->column('identifier');//开奖标识
            Cache::set('lol',$identifier,3600*24);
        }
        $randkey = array_rand($identifier,1);//暂时随机开奖 流程先完成 正式上线需要根据投注量做运算

        $result = $identifier[$randkey];//未进行人为设置

        Db::startTrans();
        try{

            $lotteryLol = LotteryLol::where('status',0)->order('index','asc')->find();
            $listenData = ['game'=>'lol','index'=>$lotteryLol['index']];//监听数据

            if( !$lotteryLol['result'] && $lotteryLol['result'] <= 0 ){
                $listen = Hook::listen("users_beted_after", $listenData);
                $lotteryLol->result = $listen[0];
            }
            $lotteryLol->result_at = date('Y-m-d H:i:s',time());
            $lotteryLol->status = 1;
            $lotteryLol->save();

            $newarr = $this->userService->upUserScore('lol',$lotteryLol->result,$lotteryLol->index);

//            if( $newarr && $newarr != false ){ //中奖通知
//                $sendClientData = [];
//                foreach ( $newarr as $k=>$v ){
//                    $sendClientData['game'] = 'lol';
//                    $sendClientData['game_name'] = '英雄五分乐';
//                    $sendClientData['status_name'] = '已中奖';
//                    $sendClientData['user_id'] = $v['user_id'];
//                    $sendClientData['zj_money'] = $v['zj'];
//                }
//                $this->sendClientMsg($sendClientData);
//                $jobHandlerClassName = 'app\api\job\SendClientMsg';
//                $jobQueueName  	  = "SendClientMsg";
//                Queue::push( $jobHandlerClassName , $sendClientData , $jobQueueName );
//            }

            $count = LotteryLol::where('status',0)->count();
            $time = time();
            if( $count < 60 ){
                for ($i=0; $i < 60-$count ; $i++) {
                    $data[] = [
                        'createtime'=> $time,
                        'game'=> 'lol',
                        'result_at' => date('Y-m-d H:i:s',$time + ((60 - $i) * 300)),
                    ];
                }
                LotteryLol::insertAll($data);//插入空数据，下一期等待开奖
            }

            Db::commit();
            return true;
        }catch (Exception $e){
            file_put_contents(
                LOTTERY_ERROR_PATH,
                '【' . date( 'Y-m-d H:i:s', $_SERVER[ 'REQUEST_TIME' ] ) . '】  LOL开奖异常'.json_encode($e->getMessage()). PHP_EOL,
                FILE_APPEND
            );
            Db::rollback();
            return FALSE;
        }


    }

    //同上
    private function randomRetDOTA2()
    {
        if( Cache::has('dota2') ){ //结果固定保存在cookie中
            $identifier = Cache::get('dota2');
        }else{
            $identifier = $this->gameDota2->column('identifier');//开奖标识
            Cache::set('dota2',$identifier,3600*24);
        }

        $lotteryRes = array_rand($identifier,1);
        $lotteryRes = $lotteryRes == 0 ? 1 :$lotteryRes;
        Db::startTrans();
        try{
            $lotteryDota2 = LotteryDota2::where('status',0)->order('index','asc')->find();
            $listenData = ['game'=>'dota2','index'=>$lotteryDota2['index']];//监听数据
            if( !$lotteryDota2['result'] && $lotteryDota2['result'] <= 0 ){
                $listen = Hook::listen("users_beted_after", $listenData);
                $lotteryDota2->result = $listen[0];
            }

            $lotteryDota2->result_at = date('Y-m-d H:i:s',time());
            $lotteryDota2->status = 1;
            $lotteryDota2->save();

            $newarr = $this->userService->upUserScore('dota2',$lotteryDota2->result,$lotteryDota2->index);

//            if( $newarr && $newarr != false ){ //中奖通知
//                $sendClientData = [];
//                foreach ( $newarr as $k=>$v ){
//                    $sendClientData['game'] = 'dota2';
//                    $sendClientData['game_name'] = '刀塔五分乐';
//                    $sendClientData['status_name'] = '已中奖';
//                    $sendClientData['user_id'] = $v['user_id'];
//                    $sendClientData['zj_money'] = $v['zj'];
//                }
//                $jobHandlerClassName = 'app\api\job\SendClientMsg';
//                $jobQueueName  	  = "SendClientMsg";
//                Queue::push( $jobHandlerClassName , $sendClientData , $jobQueueName );
//            }

            $time = time();
            $count = LotteryDota2::where('status',0)->count();
            if( $count < 100 ){
                for ($i=0; $i < 100-$count ; $i++) {
                    $data[] = [
                        'createtime'=> time(),
                        'result_at' => date('Y-m-d H:i:s',$time + ((100 - $i) * 300)),
                    ];
                }
                LotteryDota2::insertAll($data);//插入空数据，下一期等待开奖
            }
            Db::commit();
            return true;
        }catch (Exception $e){
            file_put_contents(
                LOTTERY_ERROR_PATH,
                '【' . date( 'Y-m-d H:i:s', $_SERVER[ 'REQUEST_TIME' ] ) . '】  DOTA2开奖异常'.json_encode($e->getMessage()). PHP_EOL,
                FILE_APPEND
            );
            Db::rollback();
            return FALSE;
        }

    }
    //同上
    private function randomRetPVP()
    {
        if( Cache::has('pvp') ){ //结果固定保存在Cache中
            $identifier = Cache::get('pvp');
        }else{
            $identifier = $this->gamePvp->column('identifier');//开奖标识
            Cache::set('pvp',$identifier,3600*24);
        }

        $randkey = array_rand($identifier,1);
        $lotteryRes = $identifier[$randkey];
        Db::startTrans();
        try{

            $lotteryPvp = LotteryPvp::where('status',0)->order('index','asc')->find();
            $listenData = ['game'=>'pvp','index'=>$lotteryPvp['index']];//监听数据

            if( !$lotteryPvp['result'] && $lotteryPvp['result'] <= 0 ){//手动开奖
                $listen = Hook::listen("users_beted_after", $listenData);
                $lotteryPvp->result = $listen[0];
            }

            $lotteryPvp->result_at = date('Y-m-d H:i:s',time());
            $lotteryPvp->status = 1;
            $lotteryPvp->save();

            $newarr = $this->userService->upUserScore('pvp',$lotteryPvp->result,$lotteryPvp->index);//上下分，且修改用户投注状态
//            if( $newarr && $newarr != false ){ //中奖通知
//                $sendClientData = [];
//                $jobHandlerClassName = 'app\api\job\SendClientMsg';
//                $jobQueueName  	  = "SendClientMsg";
//                foreach ( $newarr as $k=>$v ){
//                    $sendClientData['game'] = 'pvp';
//                    $sendClientData['game_name'] = '王者五分乐';
//                    $sendClientData['status_name'] = '已中奖';
//                    $sendClientData['user_id'] = $v['user_id'];
//                    $sendClientData['zj_money'] = $v['zj'];
//                    Queue::push( $jobHandlerClassName , $sendClientData , $jobQueueName );
//                }
//            }

            $count = LotteryPvp::where('status',0)->count();
            $time = time();
            if( $count < 10 ){
                for ($i=0; $i < 10-$count ; $i++) {
                    $data[] = [
                        'createtime'=> $time,
                        'result_at' => date('Y-m-d H:i:s',$time + ((10 - $i) * 60)),
                        'game'=> 'pvp',
                    ];
                }
                LotteryPvp::insertAll($data);//插入空数据，下一期等待开奖
            }

            Db::commit();
            return true;
        }catch (Exception $e){
            file_put_contents(
                LOTTERY_ERROR_PATH,
                '【' . date( 'Y-m-d H:i:s', $_SERVER[ 'REQUEST_TIME' ] ) . '】  PVP开奖异常'.json_encode($e->getMessage()). PHP_EOL,
                FILE_APPEND
            );
            Db::rollback();
            return FALSE;
        }

    }


    /**
     * 获取游戏赔率
     * @param $gameodds string 游戏
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function getGameOdds($gameodds)
    {
        $data = [];
        $lolConfig = collection(Config::where('group',$gameodds)->select())->toArray();

        foreach ($lolConfig as $item){
            $value = json_decode($item['value'],true);
            foreach ( $value as $k=>&$v ){
                $data[$k] = $v;
            }

        }
        foreach ($data as $k1=> $v1){

            if($k1 === 0 ){
                $data['magic_n'] = $v1;
                unset($data[$k1]);
            }elseif($k1 === 1){
                $data['magic_y'] = $v1;
                unset($data[$k1]);
            }
        }
        return $data;
    }

    /**
     * 将开奖结果保存缓存中，并从缓存中取
     * @param $game string 游戏
     * @param $cacheSection 缓存生成区间
     */
    protected function isLotteriesCache($game,$cacheSection)
    {
        $now = time();
        $cacheName = 'lotteries'.$game;
        $gameModel = 'Lottery'.ucfirst($game);
        $gameModel = '\app\common\model\\'.$gameModel;

        foreach ( $cacheSection as $item ){
            if( $now >= strtotime($item[0]) && $now <= strtotime($item[1]) ){//判断是否在缓存中取
                if( !Cache::has($cacheName) ){

                    $lotteries = collection($gameModel::with('game'.$game)->where('status',1)->order('index','desc')->limit(100)->select())->toArray();//查询100期
                    $cachetime = 180 - ($now-strtotime($item[0]));
                    if( $cachetime <= 0 ){
                        $cachetime = 1;
                    }
                    Cache::set($cacheName,$lotteries,$cachetime); //设置缓存时间
                }
                $isLotteriesCache = true;
                break;
            }else{
                $isLotteriesCache = false;
            }
        }

        if( $isLotteriesCache == false){
            $lotteries = collection($gameModel::with('game'.$game)->where('status',1)->order('index','desc')->limit(100)->select())->toArray();//查询100期
            Cache::set($cacheName,$lotteries,180); //设置缓存时间
        }
        $res = Cache::get($cacheName);
        if( ($now - strtotime($res[0]['result_at'])) > 180 ){//缓存时间过长，从数据库中获取
            $lotteries = collection($gameModel::with('game'.$game)->where('status',1)->order('index','desc')->limit(100)->select())->toArray();//查询100期
            $res = $lotteries;
        }

        return $res;
    }

    /**
     * * 获取
     * @param $gameodds 游戏赔率
     * @param $data post数据
     * @param $gameodds
     * @param $data
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function getEstimates($gameodds,$data)
    {
        $estimates = [];
        $odds = $this->getGameOdds($gameodds);
        $yujincome = 0;
        $ztouzhu = 0;
        foreach ( $data as $k=>$v ){
            $estimates[$k] = (float)sprintf("%.2f",$v * $odds[$k]);
            $yujincome += $estimates[$k];
            $ztouzhu += (int)$v;
        }
        $estimates['ztouzhu'] = $ztouzhu;
        $estimates['yujincome'] = $yujincome;
        return $estimates;
    }

    /**
     * 推送客户端消息
     * @param $sendClientData
     */
    protected function sendClientMsg($sendClientData)
    {
        $username = Db::name('user')->where('id',$sendClientData['user_id'])->value('username');
        $sendClientData['user'] = ['username'=>$username];
        Http::get(SecretSys::WEBSOCKET_HTTP_URL,[
            'type'  => 'zj',
            'info'  => $sendClientData
        ]);
    }

    /**
     * 检测房间权限
     * @param $money
     * @return array
     */
    public function checkRoomAuth($money)
    {
        //roomId = 3 王者荣耀
        if( $money >= $this->vipRoom ){
            $level = ['vip','member','general'];
        }elseif( $money >= $this->memberRoom && $money < $this->vipRoom ){
            $level = ['member','general'];
        }else{
            $level = ['general'];
        }
        return $level;
    }

    /**
     * 获取英雄列表
     * @param $game
     * @return bool|mixed
     */
    public function getAllHeroByGame($game)
    {
        $cacheName = 'game_'.$game;
        $gameModel = 'Game'.ucfirst($game);
        $gameModel = '\app\common\model\\'.$gameModel;

        if( !Cache::has($cacheName) ){
            $gameHeros = collection($gameModel::all())->toArray();
            $data = Cache::set($cacheName,$gameHeros,3600*24*30);
        }else{
            $data = Cache::get($cacheName);
        }
        return $data;
    }

    /**
     * 庄家选取英雄
     * @param $game
     * @param $player_1 玩家选取英雄
     * @return array
     */
    public function banker($game,$player_1)
    {
        if( !$game ){
            return ['status' => 10001,'msg' => '请选择游戏！'];
        }
        $heros = $this->getAllHeroByGame($game);
        $isOk = false; //选择的英雄是否在当前游戏列表里
        foreach ( $heros as $k=>$v ){
            if( $v['identifier'] == $player_1 ){
                $isOk = true;
                unset($heros[$k]);
            }
        }
        if( !$isOk ){
            return ['status' => 10002,'msg' => '玩家选择英雄不在列表里！'];
        }

        $bankerPlayer = $heros[array_rand($heros)];
        //庄家选取英雄即生成对战双方
        $lotteryGame = LotteryGame::create([
            'player_1'  => $player_1,
            'player_2'  => $bankerPlayer['identifier'],
            'begin_time'=> time(),
            'game'      => $game
        ]);
        $data = [
            'sequencenum' => date('YmdH').'-'.$lotteryGame->id,
            'palyer_2'    => $bankerPlayer
        ];
        $jobHandlerClassName = 'app\api\job\HeroPk';
        $jobQueueName = 'heroPk';
        Queue::push($jobHandlerClassName,['sequencenum'=>$data['sequencenum']],$jobQueueName);//
        return ['status' => 200,'msg' => 'success','data' => $data];
    }

    /**
     * @param $sequencenum
     * @param $money
     * @param string $isBanker
     * @return array
     * @throws \Complex\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function pk($sequencenum,$money,$isBanker = 'Y')
    {
        $arr = explode('-',$sequencenum);
        $winMoney = 0;
        if( $isBanker == 'Y' ){ //双方英雄都是玩家选择

            $odds = getSysParam('odds');

            if( isset($arr[1]) && $arr[1] ){
                $id = $arr[1];
                $lotteryGame = LotteryGame::where('id',$id)->where('status','0')->find();
                if( !$lotteryGame ){
                    return ['status' => 10003,'msg'=>'未找到对应的数据'];
                }
                $user_id = $this->userService->auth->id;
                $content = [
                    'player'=> $lotteryGame['player_1'],
                    'money' => $money
                ];
                $now = time();
                $betparas = [
                    'order_sn'      => Bets::getOrderSn($lotteryGame['game']),
                    'sequencenum'   => date('YmdH').'-'.$id,
                    'user_id'       => $user_id,
                    'content'       => json_encode($content),
                    'money'         => $money,
                    'addtime'       => $now,
                    'game'          => $lotteryGame['game'],
                    'ip'            => request()->ip(),
                    'createtime'    => $now,
                ];

                Db::startTrans();
                try{
                    Bets::insert($betparas);
                    $walletService = new WalletService();
                    $record_sn = $walletService->saveWallet(-$money,$user_id,3,'下注',1,$betparas['sequencenum']);
                    $walletService->recordWaterMultiple($user_id,$money,$record_sn,$money); //记录流水

                    //$k = array_rand($identifiers); //随机一个作为赢的一方



                    $identifiers = [
                        $lotteryGame['player_1'],
                        $lotteryGame['player_2']
                    ];
                    //获取概率,0是玩家，1是庄家
                    $_round = explode(':',getSysParam('gameroundswitch'));
                    $roundPret = $this->getRandomNumbers($_round);

                    $win = $identifiers[$roundPret];
                    $rand_hp = mt_rand($this->minHp,$this->maxHp); //给赢的一方赋值，hp

                    $win_hp = $rand_hp;
                    $lost_hp = 0;

                    //寻找出输的一方
                    for ( $i = 0;$i < count($identifiers) ;$i++ ){
                        if( $i != $roundPret ){
                            $lost = $identifiers[$i];
                        }
                    }

                    if( $win == $lotteryGame['player_1'] ){ //玩家赢
                        $hp_1 = $win_hp;
                        $hp_2 = $lost_hp;
                        $winMoney = $money * (float)$odds;
                        //结算
                        $walletService->saveWallet($money*1.98,$user_id,8,'下注',1,$betparas['sequencenum']);
                        Bets::where('sequencenum',$betparas['sequencenum'])->update([
                            'status'    => 1,
                            'zj_money'  => $money * (float)$odds,
                            'result'    => '玩家胜利',
                            'updatetime'=> time(),
                        ]);
                    }

                    if( $win == $lotteryGame['player_2'] ){ //庄家胜利
                        $hp_2 = $win_hp;
                        $hp_1 = $lost_hp;
                        Bets::where('sequencenum',$betparas['sequencenum'])->update([
                            'status'    => 0,
                            'result'    => '庄家胜利',
                            'updatetime'=> time(),
                        ]);
                    }
                    $paras = [
                        'end_time'  => $now,
                        'hp_1'      => $hp_1,
                        'hp_2'      => $hp_2,
                        'win'       => $win,
                        'lost'      => $lost,
                        'status'    => 2
                    ];
                    $lotteryGame->where('id',$id)->update($paras);
                    Db::commit();
                    $data = [
                        'win'   => $win,
                        'lost'  => $lost,
                        'money' => $winMoney,
                        'hp_1'  => $hp_1,
                        'hp_2'  => $hp_2
                    ];
                    return ['status'=>200,'msg'=>'成功','data'=>$data];
                }catch (PDOException $e){
                    file_put_contents(
                        BETTING_ERROR_PATH,
                        '【' . date( 'Y-m-d H:i:s', $_SERVER[ 'REQUEST_TIME' ] ) . '】  pk结算失败：'.$e->getMessage(). PHP_EOL,
                        FILE_APPEND
                    );
                    Db::rollback();
                    return ['code'=>$e->getCode(),'msg'=>'服务器异常，稍后再试！'];
                }
            }
        }
    }

    /**
     * 随机出现0和1的概率
     * @param $array
     * @return int|string
     */
    public function getRandomNumbers($array)
    {
        $numAll = 10000;
        $res = 0;
        //中奖概率基数
        $num = array_sum($array) * $numAll;//当前一等奖概率1/10000
        foreach ($array as $k => $v) {
            $v = $v * $numAll; //90000
            //获取一个1到当前基数范围的随机数
            $rand = mt_rand(1, $num);//1
            if ($rand <= $v) {//1/10的概率
                //假设当前奖项$k=2,$v<=5才能中奖
                $res = $k;
                break;
            } else {
                //假设当前奖项$k=6,$v>1900,则没中六等奖,总获奖基数2000-1900,前五次循环都没中则2000-1-5-10-24-60=1900,必中6等奖,哈哈
                $num -= $v;//90000
            }
        }
        return $res;
    }
}